%-*-Prolog-*-

% Es un pequeño compilador, que pasa a lenguaje objeto de una máquina virtual
% un lenguaje de expresiones aritméticas básicas. Además se implementa la máquina
% virtual que ejecuta dicho lenguaje objeto.

%--------------- TABLA DE SÍMBOLOS -------------------

% listas de pares

pares([]).

% valorTs(+Lista,+Clave,-Valor)
valorTs([(Clave,Valor)|_],Clave,Valor):-!.
valorTs([_|Pares],Clave,Valor):-valor(Pares,Clave,Valor).

% escribirValorTs(+Lista,+Clave,+Valor,-Lista)
escribirValorTs([],Clave,Valor,[(Clave,Valor)]).
escribirValorTs([(Clave,_)|Pares],Clave,Valor,[(Clave,Valor)|Pares]):-!.
escribirValorTs([ParA|Pares],Clave,Valor,[ParA|NPares]):-
	escribirValorTs(Pares,Clave,Valor,NPares).

% registros de propiedades para la tabla de símbolos

propiedades(Ps):-pares(Ps).
propiedad(Propiedades,Propiedad,Valor):-
	valorTs(Propiedades,Propiedad,Valor).

escribirPropiedad(Propiedades,Propiedad,Valor,NPropiedades):-
	escribirValorTs(Propiedades,Propiedad,Valor,NPropiedades).

%--------------- MÁQUINA VIRTUAL ---------------------

%Predicado para recuperar instrucciones del programa

instr([I|_],0,I):-!.
instr([_|Is],N,I):-
	NN is N-1,
	instr(Is,NN,I).

%Predicados para leer y escribir datos de la memoria de la máquina

leer([(Dir,Val)|_],Dir,Val):-!.
leer([_|M],Dir,Val):-lee(M,Dir,Val).
leer([],_,0).

escribir([(Dir,_)|Ms],Dir,Val,[(Dir,Val)|Ms]):-!.
escribir([(M|Ms)],Dir,Val,[M|NMs]):-escribir(Ms,Dir,Val,NMs).
escribir([],Dir,Val,[(Dir,Val)]).


%Representación del estado de la máquina virtual
%
%     estado(programa,pila,memoria,cp)

estadoInicial(Programa,estado(Programa,[],[],0)).

%Predicados para recuperar cada uno de los elementos del estado de la máquina

programa(estado(Programa,_,_,_),Programa).
pila(estado(_,Pila,_,_),Pila).
memoria(estado(_,_,Memoria,_),Memoria).
cp(estado(_,_,_,CP),CP).

%Predicados para actualizar los elementos del estado : pila,memoria,cp.

ponPila(estado(Programa,_,Memoria,CP),Pila,estado(Programa,Pila,Memoria,CP)).
ponMemoria(estado(Programa,Pila,_,CP),Memoria,estado(Programa,Pila,Memoria,CP)).
ponCP(estado(Programa,Pila,Memoria,_),CP,estado(Programa,Pila,Memoria,CP)).

%Predicado para consumir la cabecera de la pila

%Si intenta consumir la cabecera de pila vacía devuelve falso.
consumePila([N|Ps],Ps,N).

%Implementación de las instrucciones de la máquina

%apila(número)

ejecutaInstr(apila(N),E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	ponPila(E0,[N|Pila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%apila-dir(dirección)

ejecutaInstr(apila_dir(Dir),E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	memoria(E0,Memoria),
	leer(Memoria,Dir,Val),
	ponPila(E0,[Val|Pila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%desapila-dir(dirección)

ejecutaInstr(desapila_dir(Dir),E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	memoria(E0,Memoria),
        consumePila(Pila,NPila,Val),
	ponPila(E0,NPila,E1),
	escribir(Memoria,Dir,Val,_NMemoria),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%suma

ejecutaInstr(suma,E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 + Val1,
	ponPila(E0,[N|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%resta

ejecutaInstr(resta,E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 - Val1,
	ponPila(E0,[N|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%multiplica

ejecutaInstr(multiplica,E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 * Val1,
	ponPila(E0,[N|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%divide

ejecutaInstr(divide,E0,E2):-
	pila(E0,Pila),
	cp(E0,CP),
	consumePila(Pila,NPila,Val1),
	consumePila(NPila,NNPila,Val2),
	N is Val2 / Val1,
	ponPila(E0,[N|NNPila],E1),
	NCP is CP+1,
	ponCP(E1,NCP,E2).

%Implementación del ciclo de ejecución

ejecuta(Estado,Final):-
	programa(Estado,Programa),
	cp(Estado,CP),
	instr(Programa,CP,Instr),
	Instr\=stop,
	!,
	ejecutaInstr(Instr,Estado,NEstado),
	ejecuta(NEstado,Final).
ejecuta(Estado,Estado).

run(Programa,Resultado):-
	estadoInicial(Programa,Estado),
	ejecuta(Estado,Resultado).




%------------------COMPILADOR : TRADUCE LAS EXPRESIONES ARITMÉTICAS ----------------------
%------------------             AL LENGUAJE DE LA MÁQUINA VIRTUAL   ----------------------

%Exp ::= Term OpAd Exp | Term

exp(C0) --> term(C1),opAd(Op),exp(C2),
	{append(C1,C2,CI),append(CI,[Op],C0)}.

exp(C) --> term(C).

%Term ::= Fact OpMult Term | Fact

term(C0) --> fact(C1),opMul(Op),term(C2),
	{append(C1,C2,CI),append(CI,[Op],C0)}.

term(C) --> fact(C).

%Fact ::= Num | (Exp)

fact([apila(N)]) --> [num(N)].
fact(C) --> ['('],exp(C),[')'].

opAd(suma) --> [+].
opAd(resta) --> [-].
opMul(multiplica) --> [*].
opMul(divide) --> [/].




